﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using Ue.Core.FileSystems;
using Ue.Eagle.Runtime.FileSystems;
using Ue.Eagle.Runtime.FileSystems.Dependencies;
using Ue.Eagle.Runtime.Models;

namespace Ue.Eagle.Runtime.Loaders
{
    /// <summary>
    /// 用于加载插件dll文件存放于~/bin目录的插件
    /// </summary>
    public class ReferencedPluginLoader : PluginLoaderBase
    {
        private readonly string assemblyBasePath;
        private readonly IRootFolder rootFolder;
        private readonly IBuildManager buildManager;

        public ReferencedPluginLoader(IDependenciesFolder dependenciesFolder, IRootFolder rootFolder, IBuildManager buildManager)
            : this(dependenciesFolder, rootFolder, buildManager, "bin") { }

        public ReferencedPluginLoader(
            IDependenciesFolder dependenciesFolder, 
            IRootFolder rootFolder, 
            IBuildManager buildManager,
            string assemblyBasePath)
            : base(dependenciesFolder) 
        {
            this.rootFolder = rootFolder;
            this.buildManager = buildManager;
            this.assemblyBasePath = assemblyBasePath;
        }

        public bool Disabled { get; set; }

        public override int Priority { get { return 20; } }

        public override void PluginDeactivated(PluginLoadingContext ctx, PluginDescriptor extension)
        {
            this.DeleteAssembly(ctx, extension.Id);
        }

        public override void PluginRemoved(PluginLoadingContext ctx, DependencyDescriptor dependency)
        {
            this.DeleteAssembly(ctx, dependency.Name);
        }

        private void DeleteAssembly(PluginLoadingContext ctx, string moduleName)
        {
            var assemblyPath = this.rootFolder.CombineToPhysicalPath(assemblyBasePath, moduleName + ".dll");
            if (this.rootFolder.FileExists(assemblyPath))
            {
                ctx.DeleteActions.Add(() => File.Delete(assemblyPath));
                ctx.RestartAppDomain = true;
            }
        }

        public override PluginProbeEntry Probe(PluginDescriptor descriptor)
        {
            if (Disabled)
                return null;

            var assembly = this.buildManager.GetReferencedAssembly(descriptor.Id);
            if (assembly == null)
                return null;

            var assemblyPath = this.rootFolder.CombineToAppPath(assemblyBasePath, descriptor.Id + ".dll");

            return new PluginProbeEntry
            {
                Descriptor = descriptor,
                Loader = this,
                Priority = 100, // 最高优先级，因为~/bin 目录总是最先考虑的。
                VirtualPath = assemblyPath,
                VirtualPathDependencies = new[] { assemblyPath },
            };
        }

        protected override Plugin LoadWorker(PluginDescriptor descriptor)
        {
            if (Disabled)
                return null;

            var assembly = this.buildManager.GetReferencedAssembly(descriptor.Id);
            if (assembly == null)
                return null;

            return new Plugin
            {
                Descriptor = descriptor,
                Assembly = assembly,
                ExportedTypes = assembly.GetExportedTypes()
            };
        }
    }
}
